/*!
 * jQuery Mobile v1.0b2
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
/*!
 * jQuery UI Widget @VERSION
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Widget
 */


/*
* jQuery Mobile Framework : "slider" plugin
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* Forked by Elmundio87 to add vertical orientation
* http://jquery.org/license
*/

(function ($, undefined) {

    $.widget("mobile.slider", $.mobile.widget, {
        options: {
            theme: null,
            trackTheme: null,
            disabled: false,
            initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
            mini: false
        },

        _create: function () {

            // TODO: Each of these should have comments explain what they're for
            var self = this,

			control = this.element,

			parentTheme = $.mobile.getInheritedTheme(control, "c"),

			theme = this.options.theme || parentTheme,

			trackTheme = this.options.trackTheme || parentTheme,

			cType = control[0].nodeName.toLowerCase(),

			selectClass = (cType == "select") ? "ui-slider-switch" : "",

			controlID = control.attr("id"),

			labelID = controlID + "-label",

			label = $("[for='" + controlID + "']").attr("id", labelID),

			val = function () {
			    return cType == "input" ? parseFloat(control.val()) : control[0].selectedIndex;
			},

			min = cType == "input" ? parseFloat(control.attr("min")) : 0,

			max = cType == "input" ? parseFloat(control.attr("max")) : control.find("option").length - 1,

			sliderOrientation = control.attr("sliderOrientation") || "horizontal",

			step = window.parseFloat(control.attr("step") || 1),

			inlineClass = (this.options.inline || control.jqmData("inline") == true) ? " ui-slider-inline" : "",

			miniClass = (this.options.mini || control.jqmData("mini")) ? " ui-slider-mini" : "",


			domHandle = document.createElement('a'),
			handle = $(domHandle),
			domSlider = document.createElement('div'),
			slider = $(domSlider),

			valuebg = control.jqmData("highlight") && cType != "select" ? (function () {
			    var bg = document.createElement('div');
			    bg.className = 'ui-slider-bg ui-btn-active ui-btn-corner-all';
			    if (sliderOrientation == 'verticalInverted') {
			        $(bg).css('position', 'absolute');
			        $(bg).css('bottom', '0');
			        $(bg).css('width', '7px');
			    }
			    return $(bg).prependTo(slider);
			})() : false,

			options;

            domHandle.setAttribute('href', "#");
            domSlider.setAttribute('role', 'application');
            domSlider.className = ['ui-slider-' + sliderOrientation, selectClass, " ui-btn-down-", trackTheme, ' ui-btn-corner-all', inlineClass, miniClass].join("");
            domHandle.className = 'ui-slider-handle-' + sliderOrientation;
            domSlider.appendChild(domHandle);

            handle.buttonMarkup({ corners: true, theme: theme, shadow: true })
				.attr({
				    "role": "slider",
				    "aria-valuemin": min,
				    "aria-valuemax": max,
				    "aria-valuenow": val(),
				    "aria-valuetext": val(),
				    "title": val(),
				    "aria-labelledby": labelID
				});

            $.extend(this, {
                slider: slider,
                handle: handle,
                valuebg: valuebg,
                dragging: false,
                beforeStart: null,
                userModified: false,
                mouseMoved: false
            });

            if (cType == "select") {
                var wrapper = document.createElement('div');
                wrapper.className = 'ui-slider-inneroffset';

                for (var j = 0, length = domSlider.childNodes.length; j < length; j++) {
                    wrapper.appendChild(domSlider.childNodes[j]);
                }

                domSlider.appendChild(wrapper);

                // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );

                // make the handle move with a smooth transition
                handle.addClass("ui-slider-handle-snapping");

                options = control.find("option");

                for (var i = 0, optionsCount = options.length; i < optionsCount; i++) {
                    var side = !i ? "b" : "a",
					sliderTheme = !i ? " ui-btn-down-" + trackTheme : (" " + $.mobile.activeBtnClass),
					sliderLabel = document.createElement('div'),
					sliderImg = document.createElement('span');

                    sliderImg.className = ['ui-slider-label ui-slider-label-', side, sliderTheme, " ui-btn-corner-all"].join("");
                    sliderImg.setAttribute('role', 'img');
                    sliderImg.appendChild(document.createTextNode(options[i].innerHTML));
                    $(sliderImg).prependTo(slider);
                }

                self._labels = $(".ui-slider-label", slider);

            }

            label.addClass("ui-slider");

            // monitor the input for updated values
            control.addClass(cType === "input" ? "ui-slider-input" : "ui-slider-switch")
			.change(function () {
			    // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
			    if (!self.mouseMoved) {
			        self.refresh(val(), true);
			    }
			})
			.keyup(function () { // necessary?
			    self.refresh(val(), true, true);
			})
			.blur(function () {
			    self.refresh(val(), true);
			});

            // prevent screen drag when slider activated
            $(document).bind("vmousemove", function (event) {
                if (self.dragging) {
                    // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
                    self.mouseMoved = true;

                    if (cType === "select") {
                        // make the handle move in sync with the mouse
                        handle.removeClass("ui-slider-handle-snapping");
                    }

                    self.refresh(event);

                    // only after refresh() you can calculate self.userModified
                    self.userModified = self.beforeStart !== control[0].selectedIndex;
                    return false;
                }
            });

            slider.bind("vmousedown", function (event) {
                self.dragging = true;
                self.userModified = false;
                self.mouseMoved = false;

                if (cType === "select") {
                    self.beforeStart = control[0].selectedIndex;
                }

                self.refresh(event);
                return false;
            })
		.bind("vclick", false);

            slider.add(document)
			.bind("vmouseup", function () {
			    if (self.dragging) {

			        self.dragging = false;

			        if (cType === "select") {

			            // make the handle move with a smooth transition
			            handle.addClass("ui-slider-handle-snapping");

			            if (self.mouseMoved) {

			                // this is a drag, change the value only if user dragged enough
			                if (self.userModified) {
			                    self.refresh(self.beforeStart == 0 ? 1 : 0);
			                }
			                else {
			                    self.refresh(self.beforeStart);
			                }

			            }
			            else {
			                // this is just a click, change the value
			                self.refresh(self.beforeStart == 0 ? 1 : 0);
			            }

			        }

			        self.mouseMoved = false;

			        return false;
			    }
			});

            slider.insertAfter(control);

            // Only add focus class to toggle switch, sliders get it automatically from ui-btn
            if (cType == 'select') {
                this.handle.bind({
                    focus: function () {
                        slider.addClass($.mobile.focusClass);
                    },

                    blur: function () {
                        slider.removeClass($.mobile.focusClass);
                    }
                });
            }

            this.handle.bind({
                // NOTE force focus on handle
                vmousedown: function () {
                    $(this).focus();
                },

                vclick: false,

                keydown: function (event) {
                    var index = val();

                    if (self.options.disabled) {
                        return;
                    }

                    // In all cases prevent the default and mark the handle as active
                    switch (event.keyCode) {
                        case $.mobile.keyCode.HOME:
                        case $.mobile.keyCode.END:
                        case $.mobile.keyCode.PAGE_UP:
                        case $.mobile.keyCode.PAGE_DOWN:
                        case $.mobile.keyCode.UP:
                        case $.mobile.keyCode.RIGHT:
                        case $.mobile.keyCode.DOWN:
                        case $.mobile.keyCode.LEFT:
                            event.preventDefault();

                            if (!self._keySliding) {
                                self._keySliding = true;
                                $(this).addClass("ui-state-active");
                            }
                            break;
                    }

                    // move the slider according to the keypress
                    switch (event.keyCode) {
                        case $.mobile.keyCode.HOME:
                            self.refresh(min);
                            break;
                        case $.mobile.keyCode.END:
                            self.refresh(max);
                            break;
                        case $.mobile.keyCode.PAGE_UP:
                        case $.mobile.keyCode.UP:
                        case $.mobile.keyCode.RIGHT:
                            self.refresh(index + step);
                            break;
                        case $.mobile.keyCode.PAGE_DOWN:
                        case $.mobile.keyCode.DOWN:
                        case $.mobile.keyCode.LEFT:
                            self.refresh(index - step);
                            break;
                    }
                }, // remove active mark

                keyup: function (event) {
                    if (self._keySliding) {
                        self._keySliding = false;
                        $(this).removeClass("ui-state-active");
                    }
                }
            });

            this.refresh(undefined, undefined, true);
        },

        refresh: function (val, isfromControl, preventInputUpdate) {

            if (this.options.disabled || this.element.attr('disabled')) {
                this.disable();
            }

            var control = this.element, percent,
			cType = control[0].nodeName.toLowerCase(),
			min = cType === "input" ? parseFloat(control.attr("min")) : 0,
			max = cType === "input" ? parseFloat(control.attr("max")) : control.find("option").length - 1,
			step = (cType === "input" && parseFloat(control.attr("step")) > 0) ? parseFloat(control.attr("step")) : 1;
            sliderOrientation = control.attr("sliderOrientation") || "horizontal";

            if (typeof val === "object") {
                var data = val,
                // a slight tolerance helped get to the ends of the slider
				tol = 8;
                if (!this.dragging ||
					data.pageX < this.slider.offset().left - tol ||
					data.pageX > this.slider.offset().left + this.slider.width() + tol) {
                    return;
                }



                if (sliderOrientation == "horizontal") {
                    percent = Math.round(((data.pageX - this.slider.offset().left) / this.slider.width()) * 100);
                }
                else if (sliderOrientation == "vertical") {
                    percent = Math.round(((data.pageY - this.slider.offset().top) / this.slider.height()) * 100);
                }
                else if (sliderOrientation == "verticalInverted") {
                    percent = 100 - Math.round(((data.pageY - this.slider.offset().top) / this.slider.height()) * 100);
                }


            } else {
                if (val == null) {
                    val = cType === "input" ? parseFloat(control.val() || 0) : control[0].selectedIndex;
                }
                percent = (parseFloat(val) - min) / (max - min) * 100;
            }

            if (isNaN(percent)) {
                return;
            }

            if (percent < 0) {
                percent = 0;
            }

            if (percent > 100) {
                percent = 100;
            }

            var newval = (percent / 100) * (max - min) + min;

            //from jQuery UI slider, the following source will round to the nearest step
            var valModStep = (newval - min) % step;
            var alignValue = newval - valModStep;

            if (Math.abs(valModStep) * 2 >= step) {
                alignValue += (valModStep > 0) ? step : (-step);
            }
            // Since JavaScript has problems with large floats, round
            // the final value to 5 digits after the decimal point (see jQueryUI: #4124)
            newval = parseFloat(alignValue.toFixed(5));

            if (newval < min) {
                newval = min;
            }

            if (newval > max) {
                newval = max;
            }


            if (sliderOrientation == "horizontal") {
                this.handle.css("left", percent + "%");
            }
            else if (sliderOrientation == "vertical") {
                this.handle.css("top", percent + "%");
            }
            else if (sliderOrientation == "verticalInverted") {
                this.handle.css("top", 100 - percent + "%");
            }


            this.handle.attr({
                "aria-valuenow": cType === "input" ? newval : control.find("option").eq(newval).attr("value"),
                "aria-valuetext": cType === "input" ? newval : control.find("option").eq(newval).getEncodedText(),
                title: cType === "input" ? newval : control.find("option").eq(newval).getEncodedText()
            });
            if (sliderOrientation == 'verticalInverted') {
                this.valuebg && this.valuebg.css("height", percent + "%");
            }
            else {
                this.valuebg && this.valuebg.css("width", percent + "%");
            }


            // drag the label widths
            if (this._labels) {
                var handlePercent = this.handle.width() / this.slider.width() * 100,
				aPercent = percent && handlePercent + (100 - handlePercent) * percent / 100,
				bPercent = percent === 100 ? 0 : Math.min(handlePercent + 100 - aPercent, 100);

                this._labels.each(function () {
                    var ab = $(this).is(".ui-slider-label-a");
                    $(this).width((ab ? aPercent : bPercent) + "%");
                });
            }

            if (!preventInputUpdate) {
                var valueChanged = false;

                // update control"s value
                if (cType === "input") {
                    valueChanged = control.val() !== newval;
                    control.val(newval);
                } else {
                    valueChanged = control[0].selectedIndex !== newval;
                    control[0].selectedIndex = newval;
                }
                if (!isfromControl && valueChanged) {
                    control.trigger("change");
                }
            }
        },

        enable: function () {
            this.element.attr("disabled", false);
            this.slider.removeClass("ui-disabled").attr("aria-disabled", false);
            return this._setOption("disabled", false);
        },

        disable: function () {
            this.element.attr("disabled", true);
            this.slider.addClass("ui-disabled").attr("aria-disabled", true);
            return this._setOption("disabled", true);
        }

    });

    //auto self-init widgets
    $(document).bind("pagecreate create", function (e) {
        $.mobile.slider.prototype.enhanceWithin(e.target, true);
    });

})(jQuery);